/*
 * Licensed under the MIT License
 * 
 * Copyright (c) 2010 Kay Kreuning
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * http://code.google.com/p/as3hyvesapi
 */
package nl.hyves.api.delegates
{
	import com.serialization.json.JSON;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.system.Security;
	import flash.utils.ByteArray;
	import flash.utils.Timer;
	
	import nl.hyves.api.HyvesService;
	import nl.hyves.api.errors.HyvesError;
	import nl.hyves.api.methods.PublicMethods;
	import nl.hyves.api.utils.MultipartUtil;

	public class UploadDelegate
	{
		protected var service:HyvesService;
		protected var media:ByteArray;
		protected var albumId:String;
		protected var timer:Timer;
		protected var statusCallback:Function;
		protected var progressCallback:Function;
		protected var uploadToken:String;
		protected var uploadIP:String;
		protected var mediaId:String;
		
		/**
		 * Title of the media, shows up in the Hyves album
		 */
		public var title:String;
		
		/**
		 * Description of the media, shows up in the Hyves album
		 */
		public var description:String;
		
		/**
		 * Angle to rotate the media by, only works on images, can be either 0, 90, 180 or 270, default is 0.
		 */
		public var angle:uint;
		
		/**
		 * GPS coördinates of where the media was taken or should be associated with, shows up in the Hyves album
		 */
		public var geoData:Point;
		
		/**
		 * Construct a new UploadDelegatge instance.
		 * 
		 * @param service HyvesService instance to use for calls
		 * @param media ByteArray encoded media to upload
		 * @param albumId Hyves album to upload the media to 
		 */
		public function UploadDelegate(service:HyvesService, media:ByteArray, albumId:String)
		{
			this.service = service;
			this.media = media;
			this.albumId = albumId;
			timer = new Timer(200);
			timer.addEventListener(TimerEvent.TIMER, statusPoll);
		}
		
		/**
		 * Start the upload process.
		 * 
		 * @param statusCallback Method to call with the results or errors of the upload
		 * @param progressCallback Method to call with the status of the upload
		 */
		public function upload(statusCallback:Function, progressCallback:Function = null):void
		{
			if (service.isConnected)
			{
				this.statusCallback = statusCallback;
				this.progressCallback = progressCallback;
	
				service.call(PublicMethods.media_getUploadToken, onUploadToken);
			}
			else
			{
				statusCallback(null, new HyvesError("upload can only be called when the HyvesService associated with this UploadDelegate is connected, connect the HyvesService first!"));
			}
		}

		protected function onUploadToken(result:Object, error:HyvesError):void
		{
			if (error)
			{
				statusCallback(null, error);
				return;
			}

			uploadToken = result.token;
			uploadIP = result.ip;

			var variables:Object = new Object();
			if (title) variables.title = title;
			if (description) variables.description = description;
			if (angle) variables.angle = angle;
			if (geoData) variables.geodata = geoData.x + "," + geoData.y;
			
			Security.allowDomain("http://" + uploadIP);

			var request:URLRequest = new URLRequest("http://" + uploadIP + "/upload?token=" + encodeURIComponent(uploadToken));
			request.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + MultipartUtil.getBoundary()));	// We must add the Content-Type header like this, or Flash will throw a SecurityError: Error #2176 if the upload is not initiated from a mouse click.
			request.method = URLRequestMethod.POST;
			request.data = MultipartUtil.getPostData("file", media, variables);

			var loader:URLLoader = new URLLoader();
			loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			loader.dataFormat = URLLoaderDataFormat.BINARY;
			loader.load(request);

			timer.start();

			statusPoll();
		}

		protected function statusPoll(event:TimerEvent = null):void
		{
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onStatus);
			loader.load(new URLRequest("http://" + uploadIP + "/status?token=" + encodeURIComponent(uploadToken)));
		}

		protected function onStatus(event:Event):void
		{
			if (timer.running)
			{
				handleStatus(JSON.deserialize(event.target.data));
			}
		}

		protected function handleStatus(status:Object):void
		{
			var currentstatus:String = status.data[uploadToken][0].currentstate;

			if (currentstatus == "done")
			{
				timer.stop();

				mediaId = status.data[uploadToken][0].done.mediaid;

				service.call(PublicMethods.albums_addMedia, onAddMedia, {albumid:albumId, mediaid:mediaId});
			}
			else if (progressCallback != null)
			{
				progressCallback(status, null);
			}
		}

		protected function onIOError(event:IOErrorEvent):void
		{
			timer.stop();

			statusCallback(null, new HyvesError(event.text));
		}

		protected function onAddMedia(result:Object, error:HyvesError):void
		{
			if (error)
			{
				statusCallback(null, error);
				return;
			}

			statusCallback(result, null);
		}
	}
}
